#pragma once
#include <highgui.h>
#include "BaseVideoManager.h"
#include "Common.h"
#include "Patch.h"
#include "LivingPatch.h"

class FeaturePlayer : public BaseVideoManager
{
protected:
	static const int MaxPointsCount = 200;	

	vector<Patch*> _knownFeatures;

	vector<Patch*> _foundFeatureSet;
	vector<Patch*> _newFeatureSet;

	vector<cv::Point2f> _featurePoints;

	IplImage* _gray;
	CvMat* _derv;

	bool _isFirstFrame;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

public:
	FeaturePlayer(const char* videoFilePath) : BaseVideoManager(videoFilePath)
	{		
		_featurePoints.reserve(MaxPointsCount);
		_isFirstFrame = true;
	}
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	virtual void OnFrame(FrameKey* key, IplImage* frame)
	{
		if (_isFirstFrame)
		{
			_gray = cvCreateImage(cvGetSize(frame), 8, 1);
			_derv = cvCreateMat(frame->height - 1, frame->width - 1, CV_8U);		
		}
		cvCvtColor(frame, _gray, CV_BGR2GRAY);

		// temp collection to drawing
		_foundFeatureSet.clear();
		_newFeatureSet.clear();

		_featurePoints.clear();
		cv::goodFeaturesToTrack(_gray, _featurePoints, MaxPointsCount, 0.001, 5);
		Common::cvCalculateDerivativeMap(_derv, _gray);

		// create Patches for present frame		
		for (vector<cv::Point2f>::const_iterator it = _featurePoints.begin(); it != _featurePoints.end(); ++it)
		{		
			// looking up each feacher from new frame
			Patch* newFeature = Patch::Create(NULL, _derv, (*it).x, (*it).y);
			if (newFeature != NULL)
			{
				// search similarity feature in history
				Patch* similar = newFeature->GetMostSimilar(&_knownFeatures, 50);
				if (similar != NULL)
				{				
					// updated feature update his ttl
					similar->Update(key, newFeature);
					// no more needs
					delete newFeature;
					
					_foundFeatureSet.push_back(similar);
				}
				else
				{
					// this is a new feature				
					_newFeatureSet.push_back(newFeature);
				}
			}
		}

		for (vector<Patch*>::const_iterator it = _newFeatureSet.begin(); it != _newFeatureSet.end(); ++it)
			_knownFeatures.push_back(*it);

		// Reduce life time of known features
		int deleted = 0;
		if (!_isFirstFrame)
		{
			int i = 0; 
			while (i < _knownFeatures.size())
			{
				Patch* p = _knownFeatures[i];
				if (p->ReduceTTL())
				{
					delete p;					
					deleted++;

					_knownFeatures.erase(_knownFeatures.begin() + i);
				}
				else
					i++;
			}
		}

		// print log information
		printf("known: %i, found: %i, new: %i, removed: %i\r\n", _knownFeatures.size(), _foundFeatureSet.size(), _newFeatureSet.size(), deleted);
		
		// draw new and know featurec
		for (vector<Patch*>::const_iterator it = _newFeatureSet.begin(); it != _newFeatureSet.end(); ++it)
		{
			Patch* p = *it;
			cvCircle(frame, p->GetPoint(), 5, CV_RGB(255,0,0));
		}
		for (vector<Patch*>::const_iterator it = _foundFeatureSet.begin(); it != _foundFeatureSet.end(); ++it)
		{
			Patch* p = *it;
			vector<CvPoint>* points = p->GetHistory();

			if (points->size() > 1)
			{
				vector<CvPoint>::const_iterator leftIt = points->begin();
				vector<CvPoint>::const_iterator rightIt = leftIt + 1;
				while (rightIt != points->end())
				{
					cvLine(frame, *leftIt, *rightIt, CV_RGB(0,255,0));
					leftIt++;
					rightIt++;
				}
			}
			cvCircle(frame, p->GetPoint(), 5, CV_RGB(0,255,0));
		}

		cvShowImage("wnd", frame);

		if (_isFirstFrame)
			_isFirstFrame = false;		
	}
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	virtual void OnKeyPress(char key)
	{
		// print aditional information
		if (key == 'i')
		{
			printf("\r\n-----------[ Information ]------------\r\n");
			int i = 0;
			for (vector<Patch*>::const_iterator it = _knownFeatures.begin(); it != _knownFeatures.end(); ++it, i++)
			{
				Patch* p = *it;
				printf("[%i] hisotySize: %i, Age: %i\r\n",i, p->GetHistory()->size(), p->GetAge());
			}
			printf("------------------------------------------\r\n\r\n");
		}
	}

	virtual ~FeaturePlayer()
	{
		BasePatch::ClearPatches(&_knownFeatures);

		cvReleaseImage(&_gray);
		cvReleaseMat(&_derv);
	}
};